﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Von.Web.Framework;
using Von.Web.Models;
using Von.Web.Route;
using Von.Web.Structs;
using Von.Web.Tasks;

namespace Von.Web.Controllers
{
    public class AccountController : WebControllerBase
    {
        private IEmailSender emailSender = null;
        public override ApplicationDefinationBase App => Statement.App;

        #region "inherited properties"
        /// <summary>
        /// 构造函数，实例化了日志工具
        /// </summary>
        /// <param name="logger">日志实例</param>
        public AccountController(IEmailSender EmailSender)
        {
            emailSender = EmailSender;
        }
        #endregion
        #region Account helper
        private ViewResult messageView(ViewModelBase m, string msg)
        {
            m.StatusMessage = msg;
            return View(m);
        }
        private IActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction(nameof(HomeController.Index), "Home");
            }
        }
        /// <summary>
        /// 登录进系统
        /// </summary>
        /// <param name="ctx">HttpContext</param>
        /// <param name="info">人员信息</param>
        /// <returns></returns>
        private async Task loginToSystem(Microsoft.AspNetCore.Http.HttpContext ctx, IdentityUserInfo info)
        {
            ClaimsIdentity identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
            identity.Label = info.DisplayName;
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, info.ID.ToString()));
            identity.AddClaim(new Claim(ClaimTypes.Name, info.LoginName));
            identity.AddClaim(new Claim(ClaimTypes.Email, info.Email));
            identity.AddClaim(new Claim(ClaimTypes.MobilePhone, info.PhoneNumber));
            identity.AddClaim(new Claim("DisplayName", info.DisplayName));
            await ctx.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
            //await ctx.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(info));
        }
        #endregion
        #region "登录"
        /// <summary>
        /// 登錄
        /// </summary>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        public IActionResult Login(string returnUrl = null)
        {
            Log.Debug("Login");
            TempData["returnUrl"] = returnUrl;
            ViewBag.PWDRegex = App.AppSetting["PasswordStrength"] == null
                    ? @"(?=.*[0-9])(?=.*[a-zA-Z])(?=([\x21-\x7e]+)[^a-zA-Z0-9]).{8,30}"
                    : App.AppSetting["PasswordStrength"].ToString();
            ViewBag.PWDRequirement = App.AppSetting["PasswordRequirement"] == null
                    ? @"密码必须包含数字、小写或大写字母、特殊字符、字符数在8-30之间。"
                    : App.AppSetting["PasswordRequirement"].ToString();
            return View();
        }
        /// <summary>
        /// 登錄系統
        /// </summary>
        /// <param name="user"></param>
        /// <param name="returnUrl"></param>
        /// <returns></returns>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Login(LoginViewModel user, string returnUrl = null)
        {
            ViewData["ReturnUrl"] = returnUrl;
            IdentityUserTask userTask = new IdentityUserTask();
            IdentityUserInfo userInfo;
            if (user.LoginName.IndexOf('@') > 0) userInfo = await userTask.GetByEmail<IdentityUserInfo>(user.LoginName);
            else if (Von.Library.Functions.IsMobile(user.LoginName)) userInfo = await userTask.GetByPhoneNumber<IdentityUserInfo>(user.LoginName);
            else userInfo = await userTask.GetByLoginName<IdentityUserInfo>(user.LoginName);
            ViewBag.PWDRegex = App.AppSetting["PasswordStrength"] == null
                    ? @"(?=.*[0-9])(?=.*[a-zA-Z])(?=([\x21-\x7e]+)[^a-zA-Z0-9]).{8,30}"
                    : App.AppSetting["PasswordStrength"].ToString();
            ViewBag.PWDRequirement = App.AppSetting["PasswordRequirement"] == null
                    ? @"密码必须包含数字、小写或大写字母、特殊字符、字符数在8-30之间。"
                    : App.AppSetting["PasswordRequirement"].ToString();
            if (userInfo == null) return messageView(user, "用户名不存在或密码错误");
            if (!await userTask.CheckPassword(userInfo, user.Password))
            {
                ModelState.AddModelError("0108", "用户名不存在或密码错误");
                userTask.LoginFaild(userInfo.ID);
                user.StatusMessage = "用户名不存在或密码错误";
                return View(user);
            }
            if (userInfo.LockoutEnabled && userInfo.LockoutEnd > System.DateTime.Now)
            {
                ModelState.AddModelError("0108", "用户名不存在或密码错误");
                userTask.LoginFaild(userInfo.ID);
                user.StatusMessage = "用户名不存在或密码错误";
                return View(user);
            }
            loginToSystem(HttpContext, userInfo);
            if (returnUrl == null)
            {
                returnUrl = TempData["returnUrl"]?.ToString();
            }
            await userTask.LoginSuccessed(userInfo.ID);
            return RedirectToLocal(returnUrl);
        }
        #endregion
        #region "退出"
        /// <summary>
        /// 退出系统
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [Action("{F2B0DA8D-7FBD-468D-84C4-D11DF0169CBF}", "账户管理", "退出系统", EControlKind.CK_None)]
        public async Task<IActionResult> Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return RedirectToAction(nameof(HomeController.Index), "Home");
        }
        #endregion
        #region "注册"
        /// <summary>
        /// 注册用户子页面
        /// </summary>
        /// <returns></returns>
        public IActionResult RegisterView()
        {
            RegisterViewModel m = new RegisterViewModel();
            return View(m);
        }
        /// <summary>
        /// 注册用户Ajax服务
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        [Action("{97512807-A256-47C6-92F5-E92E41B29D7F}", "账户管理", "注册用户", EControlKind.CK_None)]
        public async Task<ExecuteResult> RegistUser(JsonStr Param)
        {
            Von.Library.KeyValueCollection extend = new Von.Library.KeyValueCollection();
            IdentityUserInfo userInfo = new IdentityUserInfo();
            string aPassword = "";
            foreach (KeyValuePair<string, JToken> jp in (JObject)Param.Json)
            {
                if (jp.Key.Equals("LoginName", StringComparison.OrdinalIgnoreCase)) userInfo.LoginName = jp.Value.ToString();
                else if (jp.Key.Equals("Email", StringComparison.OrdinalIgnoreCase)) userInfo.Email = jp.Value.ToString();
                else if (jp.Key.Equals("PhoneNumber", StringComparison.OrdinalIgnoreCase)) userInfo.PhoneNumber = jp.Value.ToString();
                else if (jp.Key.Equals("DisplayName", StringComparison.OrdinalIgnoreCase)) userInfo.DisplayName = jp.Value.ToString();
                else if (jp.Key.Equals("SysFlag", StringComparison.OrdinalIgnoreCase)) userInfo.SysFlag = (IIdentity_Sys_Flag)int.Parse(jp.Value.ToString());
                else if (jp.Key.Equals("LoginName", StringComparison.OrdinalIgnoreCase)) userInfo.LoginName = jp.Value.ToString();
                else if (jp.Key.Equals("InputPassword", StringComparison.OrdinalIgnoreCase)) aPassword = jp.Value.ToString();
                else if (jp.Key.Equals("ID", StringComparison.OrdinalIgnoreCase)) { }
                else if (jp.Key.Equals("ConfirmPassword", StringComparison.OrdinalIgnoreCase)) { }
                else extend.Add(new Library.KeyValue(jp.Key, jp.Value.ToString()));
            }
            IdentityUserTask task = new IdentityUserTask();
            ExecuteResult result = await task.Check(userInfo, aPassword); //检查信息的有效性
            if (!result.Successed) return result;
            if (!await task.CheckDoubly("Email", userInfo.Email)) return ExecuteResult.Failed(Statement.App.ResxName, "1003", "邮箱");                   //检查Email是否重复
            if (!await task.CheckDoubly("PhoneNumber", userInfo.PhoneNumber)) return ExecuteResult.Failed(Statement.App.ResxName, "1003", "联系电话");   //检查联系电话是否重复
            if (!await task.CheckDoubly("LoginName", userInfo.LoginName)) return ExecuteResult.Failed(Statement.App.ResxName, "1003", "登录名");         //检查登录名是否重复
            Log.InfoFormat("RegistUser LoginName={0},Email={1},Mobile={2}", userInfo.LoginName, userInfo.Email, userInfo.PhoneNumber);
            if (await task.Add(userInfo, aPassword))
            {
                if (extend.Count > 0)
                {
                    SubsidiaryTask subTask = new SubsidiaryTask();
                    SubsidiaryInfo subInfo = new SubsidiaryInfo();
                    foreach (Library.KeyValue vl in extend)
                    {
                        subInfo.ID = 0;
                        subInfo.IdentityIdx = userInfo.ID;
                        subInfo.SubName = vl.Key;
                        subInfo.SubValue = vl.Value;
                        await subTask.Add(subInfo);
                    }
                }
                //await loginToSystem(HttpContext, userInfo);
                if(Request.Form.Files.Count > 0) try
                {   //上传文件
                    var file = Request.Form.Files[0];
                    using (MemoryStream fs = new MemoryStream())
                    {
                        file.CopyTo(fs);
                        fs.Position = 0;
                        await task.SetUserIcon(userInfo.ID, fs);
                        fs.Close();
                    }
                }
                catch (Exception e)
                {
                    return ExecuteResult.Failed(Statement.App.ResxName, "0107", "文件上传失败！" + e.Message);
                }
                return ExecuteResult.Success(userInfo);
            }
            else return ExecuteResult.Failed(Statement.App.ResxName, "0107", "存储失败！");
        }
        public static string DisplayExtractUserRegist()
        {
            if (System.IO.File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Extends", "UserExtend.html")))
            {
                string[] inlines = System.IO.File.ReadAllLines(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Extends", "UserExtend.html"));
                return string.Join("", inlines);
            }
            return "";
        }
        #endregion
        #region "忘记密码"
        public IActionResult ForgotPassword(string returnUrl = null)
        {
            TempData["returnUrl"] = returnUrl;
            ViewBag.PWDRegex = App.AppSetting["PasswordStrength"] == null
                    ? @"(?=.*[0-9])(?=.*[a-zA-Z])(?=([\x21-\x7e]+)[^a-zA-Z0-9]).{8,30}"
                    : App.AppSetting["PasswordStrength"].ToString();
            ViewBag.PWDRequirement = App.AppSetting["PasswordRequirement"] == null
                    ? @"密码必须包含数字、小写或大写字母、特殊字符、字符数在8-30之间。"
                    : App.AppSetting["PasswordRequirement"].ToString();
            return View();
        }
        /// <summary>
        /// 找回密码服务
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        [Action("{3598495C-1845-4985-B17D-61551C0894BF}", "账户管理", "找回密码服务", EControlKind.CK_None)]
        public async Task<ExecuteResult> SendMsgToLogin(JsonStr Param)
        {
            string loginValue = Param.Json["LoginValue"].ToString();
            bool FromEmail = Param.Json["FromEmail"].ToString() == "True";
            IdentityUserTask task = new IdentityUserTask();
            IdentityUserInfo id;
            if (FromEmail) id = await task.GetByEmail<IdentityUserInfo>(loginValue);
            else id = await task.GetByPhoneNumber<IdentityUserInfo>(loginValue);
            if (id == null) return ExecuteResult.Failed(Statement.App.ResxName, "0106", "您查找的用户并不存在。");
            JArray SendValue = (JArray)Param.Json["SendValue"];  //客户端产生的随机码 24
            Random random = new Random();
            int key = random.Next(899999) + 100000;    //生成随机数，根据随机数生成6位找回数字 000F423F
            byte[] findKey = new byte[12];
            random.NextBytes(findKey);
            findKey[6] = (byte)(findKey[0] ^ (key >> 16));          //0,2,4 -> findkey xor key
            findKey[8] = (byte)(findKey[2] ^ (key >> 8));
            findKey[10] = (byte)(findKey[4] ^ key);
            findKey[0] = (byte)(findKey[0] ^ (byte)SendValue[0]);   //0,2,4 -> findkey xor sendvalue
            findKey[2] = (byte)(findKey[2] ^ (byte)SendValue[1]);
            findKey[4] = (byte)(findKey[4] ^ (byte)SendValue[2]);
            string s = Von.Library.Functions.BytesToStr(findKey);
            if (FromEmail) emailSender.SendMailAsync(id.Email, "找回密码", "请及时在找回页面中录入后面这个数字，然后单击找回密码按钮，即可重新录入新的口令" + key.ToString());
            else Statement.SystemSms.SingleSend("SystemSms", id.PhoneNumber, "请及时在找回页面中录入后面这个数字，然后单击找回密码按钮，即可重新录入新的口令" + key.ToString());
            return ExecuteResult.Success(s);
        }
        [Action("{B22DD367-90D7-4123-A3EE-081C5F48B948}", "账户管理", "验证找回密钥", EControlKind.CK_None)]
        public async Task<ExecuteResult> ReciveMsgToLogin(JsonStr Param)
        {
            string loginValue = Param.Json["LoginValue"].ToString();
            bool FromEmail = Param.Json["FromEmail"].ToString() == "True";
            IdentityUserTask task = new IdentityUserTask();
            IdentityUserInfo userInfo;
            if (FromEmail) userInfo = await task.GetByEmail<IdentityUserInfo>(loginValue);
            else userInfo = await task.GetByPhoneNumber<IdentityUserInfo>(loginValue);
            if (userInfo == null) return ExecuteResult.Failed(Statement.App.ResxName, "0106", "您查找的用户并不存在。");
            string[] SendValue = Param.Json["SendValue"].ToString().Split(',');   //客户端产生的随机码 24
            byte[] findKey = Von.Library.Functions.StrToBytes(Param.Json["FindKey"].ToString());//服务端端产生的数 12
            int key = (byte)(findKey[6] ^ (findKey[0] ^ int.Parse(SendValue[0]))) << 16 | (byte)(findKey[8] ^ (findKey[2] ^ int.Parse(SendValue[1]))) << 8 | (byte)(findKey[10] ^ (findKey[4] ^ int.Parse(SendValue[2])));
            bool chked = key == int.Parse(Param.Json["RedeemValue"].ToString());
            if (chked)
            {
                await loginToSystem(HttpContext, userInfo);
            }
            return ExecuteResult.Success(chked);
        }
        #endregion
        #region "我的账户管理"
        [Authorize]
        public async Task<IActionResult> Personal(string returnUrl = null)
        {
            if (User == null)
                return RedirectToAction("login", "account");
            ViewData["ReturnUrl"] = returnUrl;
            ViewBag.PWDRegex = App.AppSetting["PasswordStrength"] == null
                    ? @"(?=.*[0-9])(?=.*[a-zA-Z])(?=([\x21-\x7e]+)[^a-zA-Z0-9]).{8,30}"
                    : App.AppSetting["PasswordStrength"].ToString();
            ViewBag.PWDRequirement = App.AppSetting["PasswordRequirement"] == null
                    ? @"密码必须包含数字、小写或大写字母、特殊字符、字符数在8-30之间。"
                    : App.AppSetting["PasswordRequirement"].ToString();
            return View();
        }
        [Authorize]
        [Action("{BBCB96E4-DBA6-4F59-9A23-AFD6D319B9FA}", "账户管理", "更改密码", EControlKind.CK_None)]
        public async Task<ExecuteResult> ChangePassword(JsonStr Param)
        {
            if (User == null) return ExecuteResult.Failed(App.ResxName, "0103");
            Log.Debug("ChangePassword");
            IdentityUserTask task = new IdentityUserTask();
            ExecuteResult result = await task.CheckPassword(Param.Json["Password"].ToString());
            if (!result.Successed) return result;
            if (await task.ChangePassword(UserInfo.ID, Param.Json["Password"].ToString()))
                return ExecuteResult.Success(true);
            return ExecuteResult.Failed(App.ResxName, "0107");
        }
        [Authorize]
        [Action("{6B303AAF-EACD-41BF-ABE8-AFDB703132C1}", "账户管理", "更改Email", EControlKind.CK_None)]
        public async Task<ExecuteResult> ChangeEmail(JsonStr Param)
        {
            if (User == null) return ExecuteResult.Failed(App.ResxName, "0103");
            Log.Debug("ChangeEmail");
            IdentityUserTask task = new IdentityUserTask();
            if (await task.CheckDoubly("Email", Param.Json["Email"].ToString()))
                return ExecuteResult.Failed(App.ResxName, "0103");
            ExecuteResult result = await task.CheckEmail(Param.Json["Email"].ToString());
            if (!result.Successed) return result;
            if (await task.ChangeEmail(UserInfo.ID, Param.Json["Email"].ToString()))
                return ExecuteResult.Success(true);
            return ExecuteResult.Failed(App.ResxName, "0107");
        }
        [Authorize]
        [Action("{E1AD4685-AC84-4E26-A65A-D508F45585A9}", "账户管理", "更改手机号码", EControlKind.CK_None)]
        public async Task<ExecuteResult> ChangeMobile(JsonStr Param)
        {
            if (User == null) return ExecuteResult.Failed(App.ResxName, "0103");
            Log.Debug("ChangeMobile");
            IdentityUserTask task = new IdentityUserTask();
            if (await task.CheckDoubly("PhoneNumber", Param.Json["Mobile"].ToString()))
                return ExecuteResult.Failed(App.ResxName, "0103");
            ExecuteResult result = await task.CheckMobile(Param.Json["Mobile"].ToString());
            if (!result.Successed) return result;
            if (await task.ChangeMobile(UserInfo.ID, Param.Json["Mobile"].ToString()))
                return ExecuteResult.Success(true);
            return ExecuteResult.Failed(App.ResxName, "0107");
        }
        [Authorize]
        [Action("{924409A0-DA58-4AF2-8885-829AC0089E06}", "账户管理", "更改显示名", EControlKind.CK_None)]
        public async Task<ExecuteResult> ChangeDisplayName(JsonStr Param)
        {
            if (User == null) return ExecuteResult.Failed(App.ResxName, "0103");
            Log.Debug("ChangeDisplayName");
            IdentityUserTask task = new IdentityUserTask();
            if (Param.Json["DisplayName"] == null || Param.Json["DisplayName"].ToString() == null)
                return ExecuteResult.Failed(App.ResxName, "0103");
            if (await task.ChangeDisplayName(UserInfo.ID, Param.Json["DisplayName"].ToString()))
                return ExecuteResult.Success(true);
            return ExecuteResult.Failed(App.ResxName, "0107");
        }
        /// <summary>
        /// 注册用户Ajax服务
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        [Authorize]
        [Action("{2E4FB215-0DD8-4A2B-96E8-7530AA241895}", "账户管理", "更改头像", EControlKind.CK_None)]
        public async Task<ExecuteResult> ChangeUserIcon(JsonStr Param)
        {
            if (Request.Form.Files.Count > 0) try
                {   //上传文件
                    IdentityUserTask task = new IdentityUserTask();
                    var file = Request.Form.Files[0];
                    Image img;
                    using (MemoryStream fs = new MemoryStream())
                    {
                        file.CopyTo(fs);
                        img = Image.FromStream(fs);
                        fs.Close();
                    }
                    using (MemoryStream ms = new MemoryStream())
                    {
                        Image imgDest = Von.Library.VonGraphic.Zoom(img, Library.VonGraphic.EImgZoomKind.IZK_FIX_MAX, 64, 64);
                        imgDest.Save(ms, ImageFormat.Png);
                        ms.Position = 0;
                        await task.SetUserIcon(UserInfo.ID, ms);
                        ms.Close();
                    }
                }
                catch (Exception e)
                {
                    return ExecuteResult.Failed(Statement.App.ResxName, "0107", "文件上传失败！" + e.Message);
                }
            return ExecuteResult.Success(true);
        }
        #endregion
    }
}
